/**
 * MethodListModel.java
 * @author: liuex
 * 2008-1-17 ����12:50:49
 *
 * note: 
 */
package org.lex.reflect.browsing.view.browser;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.LinkedList;

import javax.swing.AbstractListModel;

import org.lex.reflect.browsing.util.ClassHelper;
import org.lex.utils.Pair;


public class MethodListModel extends AbstractListModel {
	private static final Comparator<Method> cp = new Comparator<Method>() {
		public int compare(Method m1, Method m2) {
			return m1.getName().compareTo(m2.getName());
		}
	};
	private static final Comparator<Pair<Class<?>, Method>> cp2 = new Comparator<Pair<Class<?>, Method>>() {
		public int compare(Pair<Class<?>, Method> p1, Pair<Class<?>, Method> p2) {
			return p1.second().getName().compareTo(p2.second().getName());
		}
	};
	private Class<?> clazz = null;
	private boolean showInheritedMembers = false;
	private boolean sortByDefiningType = false;
	private ArrayList<String> list = null;

	public MethodListModel() {
		this.list = new ArrayList<String>();
	}

	public boolean isShowInheritedMembers() {
		return showInheritedMembers;
	}

	public void setShowInheritedMembers(boolean showInheritedMembers) {
		this.showInheritedMembers = showInheritedMembers;
		this.setClass(clazz);
	}

	public boolean isSortByDefiningType() {
		return sortByDefiningType;
	}

	public void setSortByDefiningType(boolean sortByDefiningType) {
		this.sortByDefiningType = sortByDefiningType;
		this.setClass(clazz);
	}

	public void setClass(Class<?> clazz) {
		this.clazz = clazz;
		list.clear();
		if (false == this.showInheritedMembers) {
			Method[] methods = ClassHelper.getPublicDeclaredMethods(clazz);
			Arrays.sort(methods, cp);
			for (Method m : methods)
				list.add(ClassHelper.getGenericName(m));
		} else {
			// show all inherited members
			LinkedList<Class<?>> parents = new LinkedList<Class<?>>();
			Class<?> parent = clazz;
			while (true) {
				parents.offerFirst(parent);
				parent = parent.getSuperclass();
				if (null == parent)
					break;
			}
			Iterator<Class<?>> iter = parents.iterator();
			if (false == this.sortByDefiningType) {
				// sort by alphabet
				ArrayList<Pair<Class<?>, Method>> all = new ArrayList<Pair<Class<?>, Method>>();
				while (iter.hasNext()) {
					Class<?> c = iter.next();
					Method[] ms = ClassHelper.getPublicDeclaredMethods(c);
					for (Method m : ms)
						all.add(new Pair<Class<?>, Method>(c, m));
				}
				Collections.sort(all, cp2);
				for (Pair<Class<?>, Method> p : all)
					list.add(this.getMehtodString(p.first(), p.second()));
			} else {
				// sort by defining type
				while (iter.hasNext()) {
					Class<?> c = iter.next();
					Method[] ms = ClassHelper.getPublicDeclaredMethods(c);
					Arrays.sort(ms, cp);
					for (int i = 0; i < ms.length; i++)
						list.add(this.getMehtodString(c, ms[i]));
				}
			}
		}
		this.fireContentsChanged(this, 0, list.size() - 1);
	}

	private StringBuilder buf = new StringBuilder();

	private String getMehtodString(Class<?> clazz, Method method) {
		buf.setLength(0);
		buf.append(clazz.getSimpleName()).append(" - ");
		buf.append(ClassHelper.getGenericName(method));
		return buf.toString();
	}

	public Object getElementAt(int index) {
		return list.get(index);
	}

	public int getSize() {
		return list.size();
	}
}
